package com.own.component.common.util.encrypt.util;

import com.own.component.common.util.encrypt.BaseEncryptUtil;
import com.own.constant.ConstantNumber;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Objects;

/**
 * AesUtil aes加密解密工具
 *
 * @author chenxueli
 * @date 2020/4/20 16:08
 */
public class AesUtil implements BaseEncryptUtil {

    /**
     * 用户密钥
     */
    private static final byte[] KEY_VALUE = new byte[]{
            22, 25, -35, -45, 25, 98, -55, -45, 10, 35, -45, 25,
            26, -95, 25, -65, -78, -99, 85, 45, -62, 10, -0, 11,
            -35, 48, -98, 65, -32, 14, -78, 25, 36, -56, -45, -45,
            12, 15, -35, -75, 15, -14, 62, -25, 33, -45, 55, 68, -88
    };
    /**
     * 算法参数
     */
    private static final byte[] IV = new byte[]{
            -12, 35, -25, 65, 45, -87, 95, -22, -15, 45, 55, -66, 32, 5 - 4, 84, 55
    };
    /**
     * 加密密钥
     */
    private static SecretKey key;
    /**
     * 算法参数
     */
    private static AlgorithmParameterSpec paramSpec;
    /**
     * 加密算法
     */
    private static Cipher cipher;

    static {
        try {
            // 为指定算法生成一个密钥生成器对象。
            KeyGenerator generator = KeyGenerator.getInstance("AES");
            // 使用用户提供的随机源初始化此密钥生成器，使其具有确定的密钥长度。
            generator.init(128, new SecureRandom(KEY_VALUE));
            // 使用KeyGenerator生成（对称）密钥。
            key = generator.generateKey();
            // 使用iv中的字节作为IV来构造一个 算法参数。
            paramSpec = new IvParameterSpec(IV);
            // 生成一个实现指定转换的 Cipher 对象
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加密，使用指定数据源生成密钥，使用用户数据作为算法参数进行AES加密
     *
     * @param msg 加密的数据
     * @return 加密后的数据
     */
    @Override
    public String encrypt(String msg) {
        String str = "";
        try {
            // 用密钥和一组算法参数初始化此 cipher
            cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            // 加密并转换成16进制字符串
            str = asHex(cipher.doFinal(msg.getBytes(StandardCharsets.UTF_8)));
        } catch (BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException |
                 IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 解密，对生成的16进制的字符串进行解密
     *
     * @param value 解密的数据
     * @return 解密后的数
     */
    @Override
    public String decrypt(String value) {
        try {
            cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
            return new String(cipher.doFinal(Objects.requireNonNull(asBin(value))));
        } catch (BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException |
                 IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 将字节数组转换成16进制字符串
     *
     * @param buffer 字节数组
     * @return 字符串
     */
    private String asHex(byte[] buffer) {
        StringBuilder stringBuffer = new StringBuilder(buffer.length * 2);
        int i;
        for (i = 0; i < buffer.length; i++) {
            // 小于十前面补零
            if (((int) buffer[i] & 0xff) < 0x10) {
                stringBuffer.append("0");
            }
            stringBuffer.append(Long.toString((int) buffer[i] & 0xff, 16));
        }
        return stringBuffer.toString();
    }

    /**
     * 将16进制字符串转换成字节数组
     *
     * @param src 字符串
     * @return 字节数组
     */
    private byte[] asBin(String src) {
        if (src.length() < 1) {
            return null;
        }
        byte[] encrypted = new byte[src.length() / 2];
        for (int i = 0; i < src.length() / ConstantNumber.TWO; i++) {
            // 取高位字节
            int high = Integer.parseInt(src.substring(i * 2, i * 2 + 1), 16);
            // 取低位字节
            int low = Integer.parseInt(src.substring(i * 2 + 1, i * 2 + 2), 16);
            encrypted[i] = (byte) (high * 16 + low);
        }
        return encrypted;
    }

}
